package org.jader.bolao.statistics;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.jader.bolao.Championship;
import org.jader.bolao.Game;
import org.jader.bolao.GamePlayer;
import org.jader.bolao.Player;
import org.jader.bolao.Team;
import org.jader.bolao.gui.Application;

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public class PersonalStatistics {
	
	private List<PSPlayer> players = new LinkedList<PSPlayer>();
	
	private PersonalStatistics(){}
	
	public static PersonalStatistics generate(Championship c, Team.Modality m) {
		PersonalStatistics ps = new PersonalStatistics();
		
		ps.copyPlayersAndGames(c, m);
		ps.orderByPlayerName();
		ps.orderByTotal();
		
		if (Application.DEBUG_MODE) System.out.println("PersonalStatistics::generate() => Players: "+ps.numberOfPlayers()+"  Max. games per Player: "+ps.maxGamesPerPlayer());
		return ps;
	}
	
	private void copyPlayersAndGames(Championship c, Team.Modality m) {
		Iterator<Team> itt = c.teamsIterator();
		while (itt.hasNext()) {
			Team t = itt.next();
			
			Iterator<Player> itp = t.playerIterator();
			while (itp.hasNext()) {
				Player p = itp.next();
				if (p.getModality() == m) {
					PSPlayer player = new PSPlayer(p);
					this.players.add(player);
				}
			}
		}
	}
	
	public void orderByPlayerName() {
		for (int i=0; i<players.size(); i++) {
			for (int j=i+1; j<players.size(); j++) {
				if (players.get(i).player.getName().compareTo(players.get(j).player.getName()) > 0) {
					PSPlayer temp = players.get(i);
					players.set(i, players.get(j));
					players.set(j, temp);
				}
			}
		}
	}

	public void orderByTotal() {
		for (int i=0; i<players.size(); i++) {
			for (int j=i+1; j<players.size(); j++) {
				if (players.get(i).getTotalSum() < players.get(j).getTotalSum()) {
					PSPlayer temp = players.get(i);
					players.set(i, players.get(j));
					players.set(j, temp);
				}
			}
		}
	}
	
	public int maxGamesPerPlayer() {
		int maxGames = 0;
		Iterator<PSPlayer> it = players.iterator();
		while (it.hasNext()) {
			PSPlayer p = it.next();
			if (p.numberOfPoints() > maxGames)
				maxGames = p.numberOfPoints();
		}
		return maxGames;
	}
	
	public PSPlayer getPlayer(int arg0) {
		return players.get(arg0);
	}

	public int indexOfPlayer(Object arg0) {
		return players.indexOf(arg0);
	}

	public boolean hasNoPlayers() {
		return players.isEmpty();
	}

	public Iterator<PSPlayer> playerIterator() {
		return players.iterator();
	}

	public int numberOfPlayers() {
		return players.size();
	}

	public class PSPlayer {
		private Player player = null;
		private List<Game> games = new LinkedList<Game>();
		private Float averange = new Float(0);
		private Integer totalPoints = 0;
		
		private PSPlayer(Player p) {
			this.player = p;
			// Copy only games here player plays
			Iterator<Game> it = p.team().championship().gamesIterator();
			while (it.hasNext()) {
				Game g = it.next();
				if (g.containsPlayer(this.player)) {
					this.games.add(g);
				}
			}
			refreshAvgAndTotal();
		}
		
		public int numberOfPoints() {
			return games.size();
		}
		
		public Player player() {return this.player;}

		@Override
		public boolean equals(Object arg0) {
			if (arg0 instanceof Player) {
				Player p = (Player) arg0;
				return this.player.equals(p);
			}
			return super.equals(arg0);
		}
		
		private void refreshAvgAndTotal() {
			int total = 0;
			int gameCounter = 0;
			Iterator <Game>it = games.iterator();
			while (it.hasNext()) {
				GamePlayer gp = it.next().getPlayer(this.player);
				if (gp != null && gp.getSum() > 0) {
					total += gp.getSum();
					gameCounter++;
				}
			}
			if (gameCounter > 0) {
				this.totalPoints = total;
				this.averange = new Float((float)total / (float)gameCounter);
			}
		}
		
		public Integer getTotalSum() {return this.totalPoints;}
		
		public Float getAverange() {return this.averange;}
		
		public Game getGame(int index) {
			return this.games.get(index);
		}
	}
	
	public static class PSXMLConverter implements Converter {

		@Override
		public void marshal(Object arg0, HierarchicalStreamWriter writer,
				MarshallingContext context) {
			// Initialize formatters
			SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy");
			DecimalFormat numberFormatter = new DecimalFormat();
			numberFormatter.setMaximumFractionDigits(3);
			// Get PersonalStatistics element
			PersonalStatistics ps = (PersonalStatistics) arg0;
			// Generate complete statistics xml
			writer.startNode("players");
			Iterator<PSPlayer> itp = ps.playerIterator();
			while (itp.hasNext()) {
				PSPlayer psp = itp.next();
				writer.startNode("player");
				
				writer.startNode("name");
				writer.setValue(psp.player().getName());
				writer.endNode(); //name
				
				writer.startNode("team");
				writer.setValue(psp.player().team().getName());
				writer.endNode(); //team
				
				writer.startNode("points");
				Iterator<Game> itg = psp.games.iterator();
				while (itg.hasNext()) {
					Game g = itg.next();
					writer.startNode("point");
					
					writer.startNode("sum");
					writer.setValue(Integer.toString(g.getPlayer(psp.player()).getSum()));
					writer.endNode(); //sum
					
					writer.startNode("game");
					
					writer.startNode("place");
					writer.setValue(g.getPlace());
					writer.endNode(); //place
					
					writer.startNode("date");
					writer.setValue(dateFormatter.format(g.getDate()));
					writer.endNode(); //date
					
					writer.endNode(); //game
					
					writer.endNode(); //point
				}
				writer.endNode(); //points
				
				writer.startNode("averange");
				writer.setValue(numberFormatter.format(psp.getAverange()));
				writer.endNode(); //averange
				
				writer.startNode("totalPoints");
				writer.setValue(psp.getTotalSum().toString());
				writer.endNode(); //totalPoints
				
				writer.endNode(); //player
			}
			writer.endNode();  //players
		}

		@Override
		public Object unmarshal(HierarchicalStreamReader arg0,
				UnmarshallingContext arg1) {
			// PersonalStatistics are not recovered from XML
			return null;
		}

		@SuppressWarnings("unchecked")
		@Override
		public boolean canConvert(Class arg0) {
			return arg0.equals(PersonalStatistics.class);
		}

	}
}
